# Copyright (C) Maciej Suminski <orson@orson.net.pl>
# Copyright (C) 2020 Seth Hillbrand <seth@kipro-pcb.com>
# Copyright (C) 2023 Andrew Lutsenko <anlutsenko@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you may find one here:
# https://www.gnu.org/licenses/gpl-3.0.html
# or you may search the http://www.gnu.org website for the version 3 license,
# or you may write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA


import json
import logging
import gitlab.v4.objects
from packaging import version
from app import strings

from ..version_util import (
    has_build_info,
    fix_build_info,
    get_version
)
from ..config import KICAD_VERSION
from .handler_base import HandlerBase


logger = logging.getLogger(__name__)


class CodeIssueHandler(HandlerBase):
    def labelVersion(self, issue: gitlab.v4.objects.ProjectIssue):
        issue_version = get_version(issue.description)

        if issue_version == '':
            return False

        kmaj, kmin = issue_version.split('.')[:2]

        if int(kmin) == 99:
            issue.labels.append('.'.join((str(int(kmaj) + 1), '0')))
        else:
            issue.labels.append('.'.join((kmaj, '0')))

        return True

    def handle(self, event: str, payload: dict) -> bool:
        project = self.gitlab.projects.get(payload['project']['id'])
        issue_iid = payload['object_attributes']['iid']
        action = payload['object_attributes'].get('action', "")
        issue = project.issues.get(issue_iid)
        save = False
        skip_close = False

        # issue opening handler
        if action == 'open' or action == 'reopen':

            logger.debug("Action " + action)

            fixed_build_info = fix_build_info(issue.description)

            if fixed_build_info:
                logger.debug("Fixing the build info to be verbatim")
                issue.description = fixed_build_info
                save = True

            if 'build-error' in issue.description:
                issue.labels.append('build-error')
                save = True
                skip_close = True

            if 'build-error' in issue.labels:
                skip_close = True

            if 'Sentry Issue:' in issue.description:
                skip_close = True

            if payload['object_attributes']['assignee_id'] is not None:
                skip_close = True

            # If noone is assigned to the issue, triage for version info
            if not skip_close:
                if not has_build_info(issue.description):
                    logger.debug("Closing issue due to missing version data")

                    issue.notes.create(
                        {'body': strings.ISSUE_MISSING_VERSION_INFO})

                    issue.labels.append('status::incomplete-report')
                    issue.state_event = 'close'
                    save = True

                else:
                    logger.debug("Getting data based on description")
                    curver = get_version(issue.description)

                    if curver == '':
                        logger.debug("Missing parsable version data")

                        issue.notes.create(
                            {'body': strings.ISSUE_MALFORMED_VERSION_INFO})

                        issue.labels.append('status::need-info')
                        save = True

                    elif version.parse(curver) < version.parse(KICAD_VERSION) and \
                            not action == 'reopen':
                        logger.debug("Closing issue due to old version data")

                        issue.notes.create({
                            'body': strings.ISSUE_NEWER_KICAD_AVAILABLE})

                        issue.labels.append('status::outdated')
                        issue.state_event = 'close'
                        save = True
                    elif action == 'open':
                        logger.debug("Setting milestone")

                        save |= self.labelVersion(issue)
                    if save:
                        logger.debug("This issue will be updated by saving")
        # issue closing handler
        elif action == 'close':
            logger.info("Issue %s closed" %
                        payload['object_attributes']['iid'])

            logger.debug(json.dumps(payload['object_attributes']))
            # check for closed bugs without a milestone
            if not issue.milestone and any(
                filter(lambda x: x in issue.labels,
                       ['status::fix-committed', 'status::fix-released'])):
                issue.notes.create(
                    {'body': strings.ISSUE_CLOSED_WITHOUT_MILESTONE})
                save = True

            elif payload['object_attributes']['duplicated_to_id'] is not None:
                issue.labels.append('status::duplicate')
                # msg_data = {}
                # dup_issue = None
                # logger.debug( issue.links.list() )

                # try:
                #     dup_issue = issue.links.list()[-1]
                # except Exception as e:
                #     logger.debug( 'Caught error {}'.format( e ) )
                #     dup_issue = None

                # if dup_issue is not None:
                #     msg_data = {
                #         'body': ':information_source: **Closed as a duplicate of** %s' % \
                #               dup_issue.target_issue_iid
                #     }
                # else:

                issue.notes.create({'body': strings.ISSUE_CLOSED_AS_DUPLICATE})
                save = True

        # check if the user has correctly edited the issue
        elif action == 'updated':
            fixed_build_info = fix_build_info(issue.description)

            if fixed_build_info:
                issue.description = fixed_build_info
                save = True

            # Only update reports that are tagged by the bot
            if any(filter(lambda x: x in issue.labels,
                          ['status::outdated', 'status::incomplete-report'])):

                curver = get_version(issue.description)
                if has_build_info(issue.description) and curver != '' and \
                        version.parse(curver) >= version.parse(KICAD_VERSION):
                    issue.labels = [
                        x for x in issue.labels if not x.startswith("status::")]
                    issue.labels.append('status::new')
                    issue.state_event = 'reopen'
                    self.labelVersion(issue)
                    save = True

        # user correctly reopened issue, so we remove the tags
        if action == 'reopen' and not save:
            if any(filter(lambda x: x in issue.labels,
                          ['status::outdated', 'status::incomplete-report'])):
                issue.labels = [
                    x for x in issue.labels if not x.startswith('status::')]
                issue.labels.append('status::new')
                save = True

        # always executed
        # set status and priority whenever they are missing
        if not any(filter(lambda x: x.startswith('status::'), issue.labels)):
            issue.labels.append('status::new')
            save = True

        if not any(filter(lambda x: x.startswith('priority::'), issue.labels)):
            issue.labels.append('priority::undecided')
            save = True

        if save:
            issue.save()

        return False


# def setMilestone(gl, issue):
#     # Get the KiCad main group
#     logger.debug("Setting Milestone")
#     group = gl.groups.get(6593371)
#     next_version = get_next_version(issue.description)
#     next_milestone = None
#
#     if not next_version:
#         logger.debug("Cannot find next version")
#         return False
#
#     for milestone in group.milestones.list(state='active'):
#         if next_version == milestone.title:
#             next_milestone = milestone
#             break
#
#     if next_milestone is None:
#         logger.debug("No matching milestones to " + next_version)
#         return False
#
#     #TODO: order milestones and pick next one
#     if next_milestone.expired:
#         logger.debug(f'Milestone {next_milestone.title} is expired.  Not setting')
#         return False
#
#     issue.milestone_id = milestone.id
#     logger.debug("Setting milestone to " + milestone.title)
#     return True
